﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;

namespace System.Linq
{
    public static class OrderByExtention
    {
        #region Private expression tree helpers

        private static LambdaExpression GenerateSelector<TEntity, Tkey>(Expression<Func<TEntity, Tkey>> orderbydynamic, out Type selectorResultType) where TEntity : class
        {
            var parameter = Expression.Parameter(typeof(TEntity), "Entity");
            MemberExpression propertyAccess = orderbydynamic.Body as MemberExpression;
            selectorResultType = (propertyAccess.Member as PropertyInfo).PropertyType;
            return Expression.Lambda(propertyAccess, parameter);
        }

        private static MethodCallExpression GenerateMethodCall<TEntity, Tkey>(IQueryable<TEntity> source, string methodName, Expression<Func<TEntity, Tkey>> orderbydynamic) where TEntity : class
        {
            Type type = typeof(TEntity);
            Type selectorResultType;
            LambdaExpression selector = GenerateSelector<TEntity, Tkey>(orderbydynamic, out selectorResultType);
            MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName,
                            new Type[] { type, selectorResultType },
                            source.Expression, Expression.Quote(selector));
            return resultExp;
        }
        #endregion

        private static IOrderedQueryable<TEntity> OrderBy<TEntity, Tkey>(this IQueryable<TEntity> source, Expression<Func<TEntity, Tkey>> orderbydynamic) where TEntity : class
        {
            MethodCallExpression resultExp = GenerateMethodCall<TEntity, Tkey>(source, "OrderBy", orderbydynamic);
            return source.Provider.CreateQuery<TEntity>(resultExp) as IOrderedQueryable<TEntity>;
        }

        private static IOrderedQueryable<TEntity> OrderByDescending<TEntity, Tkey>(this IQueryable<TEntity> source, Expression<Func<TEntity, Tkey>> orderbydynamic) where TEntity : class
        {
            MethodCallExpression resultExp = GenerateMethodCall<TEntity, Tkey>(source, "OrderByDescending", orderbydynamic);
            return source.Provider.CreateQuery<TEntity>(resultExp) as IOrderedQueryable<TEntity>;
        }

        private static IOrderedQueryable<TEntity> ThenBy<TEntity, Tkey>(this IOrderedQueryable<TEntity> source, Expression<Func<TEntity, Tkey>> orderbydynamic) where TEntity : class
        {
            MethodCallExpression resultExp = GenerateMethodCall<TEntity, Tkey>(source, "ThenBy", orderbydynamic);
            return source.Provider.CreateQuery<TEntity>(resultExp) as IOrderedQueryable<TEntity>;
        }

        private static IOrderedQueryable<TEntity> ThenByDescending<TEntity, Tkey>(this IOrderedQueryable<TEntity> source, Expression<Func<TEntity, Tkey>> orderbydynamic) where TEntity : class
        {
            MethodCallExpression resultExp = GenerateMethodCall<TEntity, Tkey>(source, "ThenByDescending", orderbydynamic);
            return source.Provider.CreateQuery<TEntity>(resultExp) as IOrderedQueryable<TEntity>;
        }

        //public static IQueryable<TEntity> OrderUsingSortExpression<TEntity>(this IQueryable<TEntity> source, SortExpressions<TEntity> orderExpressions) where TEntity : class
        //{
        //    if (orderExpressions != null && orderExpressions.Count > 0)
        //    {
        //        IOrderedQueryable<TEntity> result = null;
        //        bool IsFirst = true;
        //        foreach (var od in orderExpressions)
        //        {
        //            if (IsFirst)
        //            {
        //                if (od.IsDesc)
        //                {
        //                    result = source.OrderByDescending(od);
        //                }
        //                else
        //                {
        //                    result = source.OrderBy(od);
        //                }
        //                IsFirst = false;
        //            }
        //            else
        //            {
        //                if (od.IsDesc)
        //                {
        //                    result = result.ThenByDescending(od);
        //                }
        //                else
        //                {
        //                    result = result.ThenBy(od);
        //                }
        //            }
        //        }
        //        return result;
        //    }
        //    else
        //    {
        //        return source;
        //    }
        //}
    }

    public class OrderByDynamic<T, TKey>
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="isDesc"></param>
        public OrderByDynamic(Expression<Func<T, TKey>> exp, bool isDesc)
        {
            Expression = exp;
            IsDesc = IsDesc;
        }
        /// <summary>
        /// 默认 ASC
        /// </summary>
        /// <param name="exp"></param>
        public OrderByDynamic(Expression<Func<T, TKey>> exp)
            : this(exp, false)
        {
        }
        /// <summary>
        /// 排序表达式 object 必须为T的属性
        /// </summary>
        public Expression<Func<T, TKey>> Expression { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public bool IsDesc { get; set; }
    }

    public class SortExpressions<T> : IEnumerable<OrderByDynamic<T, object>>
    {
        List<OrderByDynamic<T, object>> list;

        public SortExpressions()
        {
            list = new List<OrderByDynamic<T, object>>();
        }

        public SortExpressions<T> OrderByAsc<TKey>(Expression<Func<T, object>> exp)
        {
            list.Add(new OrderByDynamic<T, object>(exp));
            return this;
        }

        public SortExpressions<T> OrderByDesc<TKey>(Expression<Func<T, object>> exp)
        {
            list.Add(new OrderByDynamic<T, object>(exp, true));
            return this;
        }

        public int Count
        {
            get { return list.Count; }
        }

        public IEnumerator<OrderByDynamic<T, object>> GetEnumerator()
        {
            return list.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}
